/******************************************************************************
 *
 * Project:		SerwerSemaforow
 * File:		Server.cpp
 * Author:		karooolek
 * Created on:	2009-12-22
 *
 ******************************************************************************/

#include "server.h"
#include "server_thread.h"

Server::Server(uint16 port, const std::list<IPAddress> &servers) :
	Thread(),
	_locks(),
	_pinging(),
	_threads(),
	_port(port), _servers(servers)
{
}

Server::~Server()
{
	while(!_threads.empty())
	{
		_threads.back()->stop();
		_threads.pop_back();
	}
}

void Server::_run()
{
	Log::instance()->info("Semaphore server is up.");

	_pinging.setInterval(5.0f);
	_pinging.start();

	for(std::list<IPAddress>::iterator i = _servers.begin();
		i != _servers.end();
		++i)
	{
		const IPAddress &ipaddr = *i;

		TCPConnection *conn = new TCPConnection(ipaddr);
		conn->setTimeout(30000);
		conn->connect();

		ClientMessage msg;
		msg.type = MSG_GREETING;
		strncpy(msg.clientName, "SERVER", 32);
		conn->send((uint8 *)&msg, sizeof(msg));

		if(!conn->receive((uint8 *)&msg, sizeof(msg)) || msg.type != MSG_SUCCESS)
		{
			Log::instance()->warn("Couldn't connect to server " + ipaddr.hostname);
			continue;
		}

		// dodaj polaczenie do pingowania
		_pinging.addConnection(conn);

		Log::instance()->info("Connected to server " + ipaddr.hostname);

		ServerThread *thread = new ServerThread(this, conn);
		_threads.push_back(thread);
		thread->_name = String("SERVER");
		thread->start();
	}

	while(!_stop) {
		// open new connection
		TCPConnection *conn = new TCPConnection(IPAddress("", _port));
		conn->setTimeout(30000);
		conn->open();

		// dodaj polaczenie do pingowania
		_pinging.addConnection(conn);

		//Log::instance()->info("New client connected.");

		ServerThread *client = new ServerThread(this, conn);
		_threads.push_back(client);
		client->start();
	}

	_pinging.stop();
	_pinging.join();

	Log::instance()->info("Semaphore server is down.");
}

void Server::newClient(const String &client)
{
	Log::instance()->info(client + " connected.");
}

void Server::deleteClient(const String &client)
{
	std::list<String> delsems;

	// sprawdz wszystkie semafory
	for(std::map<String, QueuedLock>::iterator i = _locks.begin();
		i != _locks.end();
		++i)
	{
		String semaphore = i->first;
		QueuedLock &lock = i->second;

		// zapamietaj ten semafor do odlaczenia
		if(lock.joined.count(client))
		{
			delsems.push_back(semaphore);
		}
	}

	// usun wybrane
	while(!delsems.empty())
	{
		deleteSem(delsems.back(), client);
		delsems.pop_back();
	}

	Log::instance()->info(client + " disconnected.");
}

bool Server::createSem(const String &semaphore, const String &client)
{
	QueuedLock &lock = _locks[semaphore];
	lock.lock.setTimeout(10.0f);

	lock.joined.insert(client);
	Log::instance()->info(client + " joined semaphore " + semaphore + ".");

	std::cout << "total semaphores = " << _locks.size() << " {";
	for(std::map<String, QueuedLock>::const_iterator i = _locks.begin();
		i != _locks.end(); ++i)
	{
		std::cout << i->first << " ";
	}
	std::cout << "}" << std::endl;

	return true;
}

bool Server::deleteSem(const String &semaphore, const String &client)
{
	// semafor nie istnieje/klient nie podlaczony
	if(!_locks.count(semaphore) || !_locks[semaphore].joined.count(client))
	{
		Log::instance()->info(client + " is not connected to semaphore, can't leave semaphore " + semaphore);
		return false;
	}

	QueuedLock &lock = _locks[semaphore];

	// podnies semafor jesli kilent sie rozlacza
	if(lock.active == client)
	{
		vSem(semaphore, client);
	}

	lock.queue.erase(client);
	lock.joined.erase(client);

	Log::instance()->info(client + " left semaphore " + semaphore + ".");

	// nikt nie jest podlaczony do semafora, usuwamy...
	if(!lock.joined.size())
	{
		_locks.erase(semaphore);
	}

	std::cout << "total semaphores = " << _locks.size() << std::endl;
	for(std::map<String, QueuedLock>::const_iterator i = _locks.begin();
		i != _locks.end(); ++i)
	{
		std::cout << i->first << " ";
	}
	std::cout << std::endl;

	return true;
}

bool Server::pSem(const String &semaphore, const String &client)
{
	// semafor nie istnieje/klient nie podlaczony
	if(!_locks.count(semaphore) || !_locks[semaphore].joined.count(client))
	{
		Log::instance()->info(client + " is not connected to semaphore, can't request semaphore " + semaphore);
		return false;
	}

	Log::instance()->info(client + " requests semaphore " + semaphore);

	QueuedLock &lock = _locks[semaphore];
	lock.queue.insert(client);	lock.showQueue(semaphore);
	while(!lock.lock.tryLock())
	{
//		std::cout << client << " still waiting for " << semaphore << std::endl;		

		if(!lock.queue.count(client) || checkDeadlock(client, lock.active, true))
		{
			Log::instance()->warn("Deadlock resolved. " + client + " left semaphore " + semaphore + " queue.");
			return false;
		}
	}
	lock.active = client;
	lock.queue.erase(client);	lock.showQueue(semaphore);

	Log::instance()->info(client + " got semaphore " + semaphore);

	return true;
}

bool Server::vSem(const String &semaphore, const String &client)
{
	// semafor nie istnieje/klient nie podlaczony
	if(!_locks.count(semaphore) || !_locks[semaphore].joined.count(client))
	{
		Log::instance()->info(client + " is not connected to semaphore, can't release semaphore " + semaphore);
		return false;
	}

	Log::instance()->info(client + " released semaphore " + semaphore);

	QueuedLock &lock = _locks[semaphore];
	lock.active = "";
	lock.lock.unlock();			lock.showQueue(semaphore);

	return true;
}

bool Server::checkDeadlock(const String &client, const String &suspect, bool broadcast)
{
//	std::cout << client << " suspects " << suspect << " deadlocks..." << std::endl;

	// jest zakleszczenie
	if(client == suspect)
	{
		for(std::map<String, QueuedLock>::iterator i = _locks.begin();
			i!= _locks.end();
			++i)
		{
			const String &semaphore = i->first;
			QueuedLock &lock = i->second;

			if(!lock.queue.count(suspect))
			{
				continue;
			}

			Log::instance()->warn("Deadlock found: " + client + " on semaphore " + semaphore);
			lock.queue.erase(client);
			return true;
		}

		return false;
	}

	// sprawdz na ktorym semaforze czeka podejrzany
	for(std::map<String, QueuedLock>::iterator i = _locks.begin();
		i!= _locks.end();
		++i)
	{
		const String &semaphore = i->first;
		QueuedLock &lock = i->second;

		if(!lock.queue.count(suspect))
		{
			continue;
		}

		// wyslij broadcast o podejrzeniu zakleszczenia
	//	std::cout << "broadcasting changed deadlock suspition " << std::endl;

		DeadlockMessage msg;
		msg.type = MSG_DEADLOCK;
		strncpy(msg.clientName, client.c_str(), 32);
		strncpy(msg.suspectName, lock.active.c_str(), 32);

		for(std::list<ServerThread *>::iterator i = _threads.begin();
			i != _threads.end();
			++i)
		{
			ServerThread *thread = *i;

			//wysylaj tylko do serwerow
			if(thread->_name != "SERVER" && thread->_name != "")
			{
				continue;
			}

			thread->_conn->send((uint8 *)&msg, sizeof(msg));
		}

		checkDeadlock(client, lock.active, true);

		return false;
	}

	// rozglos dalej wiadomosc
	if(broadcast)
	{
		DeadlockMessage msg;
		msg.type = MSG_DEADLOCK;
		strncpy(msg.clientName, client.c_str(), 32);
		strncpy(msg.suspectName, suspect.c_str(), 32);

		for(std::list<ServerThread *>::iterator i = _threads.begin();
			i != _threads.end();
			++i)
		{
			ServerThread *thread = *i;

			//wysylaj tylko do serwerow
			if(thread->_name != "SERVER" && thread->_name != "")
			{
				continue;
			}

			thread->_conn->send((uint8 *)&msg, sizeof(msg));
		}
	}

	return false;
}
